#include <stdio.h>
#include <assert.h>
#include <sys/time.h>
#include <errno.h>

#include "bcti_Semaphore.h"


namespace lzpLib{

Semaphore::Semaphore(unsigned int initialCount): count_(initialCount){
    pthread_mutex_init(&mutex_, NULL);
    pthread_cond_init(&cond_, NULL);
}

Semaphore::~Semaphore(){
    pthread_cond_destroy(&cond_);
    pthread_mutex_destroy(&mutex_);
}

void Semaphore::post(){
    pthread_mutex_lock(&mutex_);
    count_++;
    if (count_ >= 1) {//TODO count_>=1
        int result = pthread_cond_signal(&cond_);
        // pthread_cond_signal is only supposed to fail if the cond var was
        // not initialized, which would be a fatal error.
        if (result != 0) {
            pthread_mutex_unlock(&mutex_);
            printf("post fail\n");
            assert(0);
            return;
        }
    }
    pthread_mutex_unlock(&mutex_);
}

void Semaphore::wait(){
    pthread_mutex_lock(&mutex_);
    while (count_ <= 0) {
        int result = pthread_cond_wait(&cond_, &mutex_);
        // as above, any failure is fatal here
        if (result != 0) {
            printf("Wait fail\n");
            assert(0);
            return;
        }
    }
    count_--;
    pthread_mutex_unlock(&mutex_);
}

bool Semaphore::tryWait(){
    pthread_mutex_lock(&mutex_);
    bool sem;
    if ((sem = count_ > 0)){
        count_--;
    }
    pthread_mutex_unlock(&mutex_);
    return sem;
}

int Semaphore::timedWait(unsigned int relTimeOutMs){
    pthread_mutex_lock(&mutex_);

    timespec now;
    clock_gettime(CLOCK_REALTIME, &now);
    timespec waitUntil;
    waitUntil.tv_sec  = now.tv_sec + relTimeOutMs/ 1000;
    waitUntil.tv_nsec = now.tv_nsec + (relTimeOutMs % 1000)*1000*1000ull;

    while (count_ == 0) {
        int result = pthread_cond_timedwait(&cond_, &mutex_, &waitUntil);
        // catch a timeout
        if (result == ETIMEDOUT) {
            pthread_mutex_unlock(&mutex_);
            return 0;
        }
        // any other failure is fatal
        if (result != 0) {
            pthread_mutex_unlock(&mutex_);
            printf("timedWait fail\n");
//			assert(0);
            return -1;
        }
    }
    count_--;
    pthread_mutex_unlock(&mutex_);
    return 1;
}
}

